package org.linlinjava.litemall.core.payment.clover.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.linlinjava.litemall.core.constant.LumiereCloverAPIConstant;
import org.linlinjava.litemall.core.constant.LumiereCloverConstant;
import org.linlinjava.litemall.core.notify.NoticeHelper;
import org.linlinjava.litemall.core.payment.DefaultCurType;
import org.linlinjava.litemall.core.payment.clover.config.CloverConfig;
import org.linlinjava.litemall.core.payment.clover.dto.ApiChargeDTO;
import org.linlinjava.litemall.core.payment.clover.rq.ApiChargeRQ;
import org.linlinjava.litemall.core.payment.clover.rq.ApiRefundRQ;
import org.linlinjava.litemall.core.payment.clover.service.UserCloverService;
import org.linlinjava.litemall.core.util.AdminCouponDateUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.beans.Constants;
import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.dto.CloverChargesDTO;
import org.linlinjava.litemall.db.dto.CloverRefundsDTO;
import org.linlinjava.litemall.db.service.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.sql.Wrapper;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserCloverServiceImpl implements UserCloverService {

    private final CloverConfig cloverConfig;
    private final LumiereCloverChargesLogService lumiereCloverChargesLogService;
    private final LitemallUserService litemallUserService;
    private final LitemallUserRechargeOrderService litemallUserRechargeOrderService;
    private final LitemallRechargeTransactionsService litemallRechargeTransactionsService;
    private final LitemallRechargeConsumptionService litemallRechargeConsumptionService;
    private final LumiereCloverRefundsLogService lumiereCloverRefundsLogService;

    private final LumiereCouponStrategyService lumiereCouponStrategyService;

    private final LumiereCouponUserService lumiereCouponUserService;

    private final LumiereCouponService lumiereCouponService;

    private final NoticeHelper noticeHelper;

    @SneakyThrows
    @Override
    @Transactional
    public ResponseUtil.Response executePayment(Integer userId, BigDecimal amount, String token) {
        Lock lock = new ReentrantLock();
        try {
            lock.lock();
            //支付体
            ApiChargeRQ apiChargeRQ = new ApiChargeRQ();
            apiChargeRQ.setAmount(amount.multiply(new BigDecimal(100)));
            apiChargeRQ.setCurrency(DefaultCurType.CAD.getType());
            apiChargeRQ.setSource(token);
            //apiChargeRQ.setStored_credentials(new ApiChargeDTO("SUBSEQUENT",true,"CARDHOLDER"));
            //apiChargeRQ.setSource(cloverCard.getCustomerId());
            //开始交易
            OkHttpClient client = new OkHttpClient();

            MediaType mediaType = MediaType.parse(LumiereCloverConstant.APPLICATIONJSON);
            RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(apiChargeRQ));
            Request request = new Request.Builder()
                    .url(LumiereCloverAPIConstant.CLOVER_EXECUTEPAYMENT)
                    .post(body)
                    .addHeader(LumiereCloverConstant.ACCEPT, LumiereCloverConstant.APPLICATIONJSON)
                    .addHeader(LumiereCloverConstant.CONTENTTYPE, LumiereCloverConstant.APPLICATIONJSON)
                    .addHeader(LumiereCloverConstant.IDEMPOTENCYKEY, IdUtil.simpleUUID())
                    .addHeader(LumiereCloverConstant.AUTHORIZATION, String.format(LumiereCloverConstant.BEARER,cloverConfig.getAccessToken()))
                    .build();

            Response response = client.newCall(request).execute();
            JSONObject vo =  JSON.parseObject(response.body().string());
            CloverChargesDTO cloverChargesDTO = JSON.toJavaObject(vo,CloverChargesDTO.class);
            //支付失败
            if(StrUtil.isBlank(cloverChargesDTO.getId())){
                /*JSONObject error = JSON.parseObject(vo.getString("error"));
                String message = error.getString("message");
                if("Above maximum transaction count per card".equals(message)){
                    return ResponseUtil.fail(501,message);
                }*/
                log.info("clover card executePayment error : " + vo);
                String resultCode = StrUtil.sub(vo.getString("message"), 0, 3);
                return ResponseUtil.fail(Integer.parseInt(resultCode),vo.getString("error"));
            }
            long chargesLogId = IdUtil.createSnowflake(1, 1).nextId();
            //保存交易记录
            LumiereCloverChargesLog lumiereCloverChargesLog = LumiereCloverChargesLog.builder()
                    .id(chargesLogId)
                    .userId(userId)
                    .transactionId(cloverChargesDTO.getId())
                    .amount(cloverChargesDTO.getAmount())
                    .amountRefunded(cloverChargesDTO.getAmount_refunded())
                    .currency(cloverChargesDTO.getCurrency())
                    .created(cloverChargesDTO.getCreated())
                    .captured(cloverChargesDTO.getCaptured())
                    .refNum(cloverChargesDTO.getRef_num())
                    .authCode(cloverChargesDTO.getAuth_code())
                    .networkStatus(cloverChargesDTO.getOutcome().getNetwork_status())
                    .type(cloverChargesDTO.getOutcome().getType())
                    .paid(cloverChargesDTO.getPaid())
                    .status(cloverChargesDTO.getStatus())
                    .customerId(cloverChargesDTO.getSource().getId())
                    .addressLine1(cloverChargesDTO.getSource().getAddress_line1())
                    .addressLine1Check(cloverChargesDTO.getSource().getAddress_line1_check())
                    .addressZip(cloverChargesDTO.getSource().getAddress_zip())
                    .addressZipCheck(cloverChargesDTO.getSource().getAddress_zip_check())
                    .brand(cloverChargesDTO.getSource().getBrand())
                    .expMonth(cloverChargesDTO.getSource().getExp_month())
                    .expYear(cloverChargesDTO.getSource().getExp_year())
                    .first6(cloverChargesDTO.getSource().getFirst6())
                    .last4(cloverChargesDTO.getSource().getLast4())
                    .transactionType(2)
                    .addTime(LocalDateTime.now())
                    .deleted(false)
                    .build();
            lumiereCloverChargesLogService.save(lumiereCloverChargesLog);
            //更新账户余额
            LitemallUser litemallUser = litemallUserService.findById(userId);
            litemallUser.setAvailableAmount(litemallUser.getAvailableAmount().add(amount));
            int update = litemallUserService.updateById2(litemallUser);
            if(update==0){
                return ResponseUtil.fail("余额更新失败");
            }
            //保存充值记录
            log.info("保存充值记录===》");
            LitemallUserRechargeOrder userRechargeOrder = new LitemallUserRechargeOrder();
            userRechargeOrder.setPayType(Constants.PAY_TYPE_CLOVER);
            userRechargeOrder.setCurrency(DefaultCurType.CAD.getType());
            userRechargeOrder.setTransationId(cloverChargesDTO.getId());        //交易号，退款时需要用到
            userRechargeOrder.setPayTime(LocalDateTime.now());
            userRechargeOrder.setPayStatus(Constants.PAY_STATUS_DONE);
            litemallUserRechargeOrderService.add(userRechargeOrder);
            //充值记录
            litemallRechargeTransactionsService.save(new LitemallRechargeTransactions()
                    .setUserId(litemallUser.getId())
                    .setUsername(litemallUser.getUsername())
                    .setRechargeType(Constants.RECHARGE_TYPE_GIFT_CARD)
                    .setPayType(Constants.PAY_TYPE_CLOVER)
                    .setRechargeAmount(amount)
                    .setRechargeSource(Constants.ORDER_SOURCE_APP)
            );

            //保存余额明细
            LitemallRechargeConsumption rechargeConsumption = new LitemallRechargeConsumption();
            rechargeConsumption.setUserId(userId);
            rechargeConsumption.setMobile(litemallUser.getMobile());
            rechargeConsumption.setUsername(litemallUser.getUsername());
            rechargeConsumption.setUserLevel(litemallUser.getUserLevel());
            rechargeConsumption.setAvailableAmount(litemallUser.getAvailableAmount());
            rechargeConsumption.setPoints(litemallUser.getPoints());
            rechargeConsumption.setType((byte) 1);
            rechargeConsumption.setAmount(amount);
            rechargeConsumption.setDeleted(false);
            rechargeConsumption.setAddUserId(userId);
            rechargeConsumption.setOrderSource((byte) 1);
            rechargeConsumption.setPayType(5);
            rechargeConsumption.setAddTime(LocalDateTime.now());
            litemallRechargeConsumptionService.save(rechargeConsumption);
            //发送系统通知
            noticeHelper.noticeUser( Constants.MSG_TYPE_OTHER,LumiereCloverConstant.WX_BALANCE_CLOVER_RECHARGE_TITLE_NOTICEHELPER,
                    String.format(LumiereCloverConstant.WX_BALANCE_CLOVER_RECHARGE_NOTICEHELPER, amount), userId,rechargeConsumption);
            //赠送充值策略
            List<LumiereCouponStrategy> lumiereCouponStrategies = lumiereCouponStrategyService.list(Wrappers.lambdaQuery(LumiereCouponStrategy.class)
                    .eq(LumiereCouponStrategy::getCouponType, 7)
                    .eq(LumiereCouponStrategy::getStatus, 1));
            if (!lumiereCouponStrategies.isEmpty()){
                //保存要赠送的券名
                List couponName = CollUtil.newArrayList();
                lumiereCouponStrategies.stream().forEach(couponStrategie->{
                    //开始发券
                    if(amount.compareTo(couponStrategie.getRechargeAmount())!=-1) {
                        for (int i = 1; i <= couponStrategie.getTotal(); i++) {
                            long nextId = IdUtil.getSnowflake(2, 2).nextId();
                            LocalDateTime[] localDateTimes = AdminCouponDateUtil.calculateExpirationTime(couponStrategie);
                            LumiereCouponUser lumiereCouponUser = LumiereCouponUser.builder()
                                    .userId(litemallUser.getId())
                                    .strategyId(couponStrategie.getId())
                                    .couponId(couponStrategie.getCouponId())
                                    .userName(litemallUser.getUsername())
                                    .startTime(localDateTimes[0])
                                    .endTime(localDateTimes[1])
                                    .code(String.valueOf(nextId))
                                    .addTime(LocalDateTime.now())
                                    .updateTime(LocalDateTime.now())
                                    .deleted(false)
                                    .build();
                            LumiereCoupon lumiereCoupon = lumiereCouponService.getById(couponStrategie.getCouponId());
                            couponName.add(lumiereCoupon.getName());
                            lumiereCouponUserService.save(lumiereCouponUser);
                        }
                    }
                });
                //发送赠送券 系统消息给用户
                noticeHelper.noticeUser( Constants.MSG_TYPE_OTHER,LumiereCloverConstant.WX_BALANCE_CLOVER_RECHARGE_TITLE_NOTICEHELPER,
                        String.format(LumiereCloverConstant.WX_BALANCE_CLOVER_RECHARGE_GIVE_COUPON_NOTICEHELPER, amount,couponName), userId,rechargeConsumption);
            }

            lock.unlock();
        }catch (Exception e){
            lock.unlock();
        }
        return ResponseUtil.ok("Clover充值成功");
    }

    @SneakyThrows
    @Transactional
    @Override
    public Boolean refund(Integer orderId) {
            LumiereCloverChargesLog cloverChargesLog = lumiereCloverChargesLogService.getOne(Wrappers.lambdaQuery(LumiereCloverChargesLog.class)
                    .eq(LumiereCloverChargesLog::getOrderId, orderId));
            ApiRefundRQ apiRefundRQ = new ApiRefundRQ();
            apiRefundRQ.setCharge(cloverChargesLog.getTransactionId());
            //执行退款
            OkHttpClient client = new OkHttpClient();
            MediaType mediaType = MediaType.parse(LumiereCloverConstant.APPLICATIONJSON);
            RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(apiRefundRQ));
            Request request = new Request.Builder()
                    .url(LumiereCloverAPIConstant.CLOVER_REFUNDS)
                    .post(body)
                    .addHeader(LumiereCloverConstant.ACCEPT, LumiereCloverConstant.APPLICATIONJSON)
                    .addHeader(LumiereCloverConstant.CONTENTTYPE, LumiereCloverConstant.APPLICATIONJSON)
                    .addHeader(LumiereCloverConstant.AUTHORIZATION, String.format(LumiereCloverConstant.BEARER,cloverConfig.getAccessToken()))
                    .build();

            Response response = client.newCall(request).execute();
            JSONObject vo =  JSON.parseObject(response.body().string());
            CloverRefundsDTO cloverRefundsDTO = JSON.toJavaObject(vo,CloverRefundsDTO.class);
            //支付失败
            if(StrUtil.isBlank(cloverRefundsDTO.getId())){
                log.info("clover card executeRefunds error : " + vo);
                String resultCode = StrUtil.sub(vo.getString("message"), 0, 3);
                return false;
            }
            long chargesLogId = IdUtil.createSnowflake(1, 1).nextId();
            //保存退款记录记录
            LumiereCloverRefundsLog cloverRefundsLog=LumiereCloverRefundsLog.builder()
                    .id(chargesLogId)
                    .transactionId(cloverRefundsDTO.getId())
                    .object(cloverRefundsDTO.getObject())
                    .amount(cloverRefundsDTO.getAmount())
                    .charge(cloverRefundsDTO.getCharge())
                    .created(cloverRefundsDTO.getCreated())
                    .status(cloverRefundsDTO.getStatus())
                    .refundType(cloverRefundsDTO.getMetadata().getRefundType())
                    .authCode(cloverRefundsDTO.getMetadata().getAuthCode())
                    .refNum(cloverRefundsDTO.getMetadata().getRefNum())
                    .gatewayResponseCode(cloverRefundsDTO.getMetadata().getGatewayResponseCode())
                    .addTime(LocalDateTime.now())
                    .build();
            lumiereCloverRefundsLogService.save(cloverRefundsLog);
            return true;
    }

}
